<?php

/***  DOCUMENTATION LAYER

Greqo Analytics Class

Name: GreqoAnalytics
Version: 0.6
Last Update: Jun 2007
Author: Tom at klenwell@gmail.com

DESCRIPTION
	Programmatically access a Google Analytics account

METHODS
	MAGIC
	GreqoAnalytics($debug=0)	*php 4 constructor*
	__construct($debug)		*php 5 constructor*
	__destruct()	
	
	PUBLIC
	login()
	get_site_summary($start_uts=0, $stop_uts=0, $site_id=0)
	get_site_summary_array($start_uts=0, $stop_uts=0, $site_id=0)
	get_report($greqo_id, $start_uts=0, $stop_uts=0, $site_id=0)
	request_report_from_google($site_id, $rpt, $pdr1=0, $pdr2=0, $fmt=0, $cmp=0)
	request_account_list()
	request_profile_list($account_id=null)
	uts_to_date($uts)
	set_report_cache_lifetime($secs)
	get_session_data()
	set_session_data($DATA)
	clear_session_data()
	logout()
	
	PRIVATE
	_organize_summary($REPORT)
	_parse_report($report_data, $format)
	_parse_report_csv($raw_csv)
	_parse_report_section($raw_section_csv)
	_quick_parse_report_xml($xml_string)
	_is_report_request_prepared()
	_dump_array($ARRAY)
	_set_filename()
	_set_dirpath()
	
USAGE
	// prepare report
	require_once('../analytics.class.php');
	$GreqoAnalytics = new GreqoAnalytics($debug=1);
	$GreqoAnalytics->email = 'user@gmail.com';
	$GreqoAnalytics->password = 'password';
	$GreqoAnalytics->site_id = '0000001';      
	
	// get report
	$start_uts = time() - ( 3 * $GreqoAnalytics->day_sec );
	$stop_uts = time() - ( 2 * $GreqoAnalytics->day_sec );
	$REPORT_SUMMARY = $GreqoAnalytics->get_site_summary($start_uts, $stop_uts, $site_id=0);
	$output = print_r($REPORT_SUMMARY, 1);
	echo "<pre>$output</pre>";

NOTES
	To do: update to coordinate with newest Analytics released

	Google does not provide an official API for Analytics at this time.  This class
	uses the PEAR HTTP_Request class to mimic the browser steps necessary to display
	a report.  This is not necessarily sanctioned by Google and should be used
	advisedly.

______________________________________________________________________________*/

// Load File of Base Class
$base_fname = 'greqo.class.php';
$base_dirpath = '';
require_once($base_dirpath . $base_fname);


// GreqoAnalytics
/*____________________________________________________________________________*/
class GreqoAnalytics extends Greqo
{
/* PUBLIC PROPERTIES */
var $debug = 0;
var $class_name = __CLASS__;
var $oid = '';
var $DS = DIRECTORY_SEPARATOR;
var $REPORTS = array();
var $is_logged_in = 0;
var $email = '';
var $password = '';
var $site_id = 0;
var $GCOOKIE = array();

// Google Analytic Urls
var $url_home = 'https://www.google.com/analytics/home/';
var $url_loginbox = 'https://www.google.com/accounts/ServiceLoginBox?service=analytics&nui=1&hl=en-US&continue=http://www.google.com/analytics/home/%3Fet%3Dreset%26hl%3Den-US';
var $url_loginbox_auth = 'https://www.google.com/accounts/ServiceLoginBoxAuth';
var $url_loginbox_auth_cont = 'http://www.google.com/analytics/home/?et=reset&amp;hl=en-US';
var $url_report_request = 'https://www.google.com/analytics/reporting/export?';
var $url_query_exec = '&user=&vid=2001&ns=10&ss=0&fd=&ft=2&sf=2&sb=1&dow=0&dt=3&dtc=2&xd=1';
var $loginbox_key = 'GA3T';

// Google Analytic Constants
var $GA_FMT_XML = 1;
var $GA_FMT_CSV = 2;
var $GA_CMP_AVG = 'average';		// default cmp value
var $GA_CMP_DR = 'date_range';	// compare by date range

// other constants
var $day_sec = 86400;
var $week_sec = 604800;

/* PRIVATE PROPERTIES */
var $_filename = '';
var $_dirpath = '';


/* ** MAGIC METHODS ** */
// php4 constructor
function GreqoAnalytics($debug=0, $oid=null)
{
	$this->__construct($debug, $oid);
	register_shutdown_function( array($this, '__destruct') );
}
// END constructor

// php5 constructor
function __construct($debug=0, $oid=null)
{
	// default
	$this->debug = $debug;
	$this->oid = ( empty($oid) ) ? $this->class_name : $oid;
	$this->_set_filename();
	$this->_set_dirpath();
	
	// additional code
	parent::__construct($debug, $oid);
	
	// load cacher
	$this->load_cacher($lifetime=43200);
	
	// valid report types served by this class
	$this->REPORTS = array
	(
		// name => array( GA_name, format, compare)
		'visitors' => array('VisitorsOverviewReport', $this->GA_FMT_XML, 1),
		'sources' => array('AllSourcesReport', $this->GA_FMT_CSV, 0),
		'keywords' => array('KeywordsReport', $this->GA_FMT_CSV, 0),
		'content' => array('TopContentReport', $this->GA_FMT_CSV, 0),
	);
	
	// debug
	if ( $this->debug ) $this->print_d('debugging is active for oid ' . $this->oid);
	if ( $this->debug ) $this->print_d('constructor complete for class ' . __CLASS__);
}
// END constructor

// destructor
function __destruct()
{
	if ( $this->debug ) $this->print_d("destroying class {$this->class_name} (oid: {$this->oid})");
}
// END destructor



/* ** PUBLIC METHODS ** */
// method: login
function login($force=0)
{
	$logged_in = 0;		// return
	if ( $this->debug ) $this->print_d('login requested');
	
	// get saved session data
	$this->get_session_data();

	// already logged in?
	if ( $this->is_logged_in && !$force ) 
	{
		if ( $this->debug ) $this->print_d('already logged in and force (login) flag not set', 'orange');
		return 1;
	}
	
	// clear login data
	$this->logout();
	
	// prep check
	if ( !$this->_is_login_prepared() ) return 0;
	
	// prepare login box request (get loginbox cookie)
	$this->GreqoClient->setURL($this->url_loginbox);
	$this->GreqoClient->setMethod(HTTP_REQUEST_METHOD_GET);
	$this->GreqoResponse = $this->GreqoClient->sendRequest();
	
	// process request
	$this->GreqoResponse = $this->GreqoClient->sendRequest();
	if ( $this->debug ) $this->print_d("Google Response (loginbox: {$this->url_loginbox}):<br />" . htmlspecialchars($this->GreqoClient->getResponseBody()), '#f66');
	if ( PEAR::isError($this->GreqoResponse) )
	{
		$this->_trigger_client_error('login box auth request failed');	
		return 0;
	}	
	$GCOOKIE = $this->GreqoClient->getResponseCookies();
	$loginbox_body = $this->GreqoClient->getResponseBody();
	
	// error check
	if ( !ereg('Google Accounts', $loginbox_body) ) 
	{
		$this->_trigger_request_error('unable to connect to Google Analytics');
		return 0;
	}
	
	// get login cookie
	$loginbox_cookie = $GCOOKIE[$this->loginbox_key]['value'];
	
	// prepare login box auth request
	$this->GreqoClient->setMethod(HTTP_REQUEST_METHOD_POST);
	$this->GreqoClient->setURL($this->url_loginbox_auth);
	$this->GreqoClient->addPostData('continue', $this->url_loginbox_auth_cont);
	$this->GreqoClient->addPostData('service', 'analytics');
	$this->GreqoClient->addPostData('nui', '1');
	$this->GreqoClient->addPostData('hl', 'en-US');
	$this->GreqoClient->addPostData('GA3T', $loginbox_cookie);
	$this->GreqoClient->addPostData('Email', $this->email);
	$this->GreqoClient->addPostData('Passwd', $this->password);
	
	// process request
	$this->GreqoResponse = $this->GreqoClient->sendRequest();
	if ( $this->debug ) $this->print_d("Google Response (login box auth: {$this->url_loginbox_auth}):<br />" . htmlspecialchars($this->GreqoClient->getResponseBody()), '#6f6');
	if ( PEAR::isError($this->GreqoResponse) )
	{
		$this->_trigger_client_error('login box auth request failed');	
		return 0;
	}

  #$this->_GHEADER = $this->GreqoClient->getResponseHeader();
  $this->GCOOKIE = $this->GreqoClient->getResponseCookies();
  $loginbox_auth_body = $this->GreqoClient->getResponseBody();
	$next_url = ereg_replace(".*<a href=\"(.*)\" target=.*", "\\1", $loginbox_auth_body);
  $next_url = ereg_replace('amp;', '', $next_url);
	
	// check login
	if ( !ereg('accounts/TokenAuth', $next_url) )
	{
		$this->logout();
		$this->_trigger_request_error('login and password did not match -- check settings');
		return 0;
	}
	
	// prepare cookie check request
  $this->GreqoClient->clearPostData();
	$this->GreqoClient->setMethod(HTTP_REQUEST_METHOD_GET);
	$this->GreqoClient->setURL($next_url);
	foreach ($this->GCOOKIE as $_CD ) $this->GreqoClient->addCookie($_CD['name'], $_CD['value']);
	
	// process request
	$this->GreqoResponse = $this->GreqoClient->sendRequest();
	if ( $this->debug ) $this->print_d("Google Response (cookie check: $next_url):<br />" . htmlspecialchars($this->GreqoClient->getResponseBody()), '#66f');
	$next_url = $this->GreqoClient->getResponseHeader('Location');
	if (!$next_url) 
	{
		$this->logout();
		$this->_trigger_request_error('unable to forward to Google Analytics service page (url not returned)');
		return 0;
	}
	
	// redirect to service
	$this->GreqoClient->setURL($next_url);
	foreach ($this->GCOOKIE as $_CD ) $this->GreqoClient->addCookie($_CD['name'], $_CD['value']);
	$this->GreqoResponse = $this->GreqoClient->sendRequest();
	if ( $this->debug ) $this->print_d("Google Response (service redirect: $next_url):<br />" . htmlspecialchars($this->GreqoClient->getResponseBody()), '#f66');
	if ( PEAR::isError($this->GreqoResponse) )
	{
		$this->logout();
		$this->_trigger_client_error('login box auth request failed');	
		return 0;
	}
	
	$this->is_logged_in = 1;
	$this->set_session_data();
	return 1;
}
// END method

// method: get site summary
function get_site_summary($start_uts=0, $stop_uts=0, $site_id=0)
{
	$summary = '';	// return
	$DATA = $this->get_site_summary_array($start_uts, $stop_uts, $site_id);
	$summary = $this->_summary_array_to_string($DATA);
	return $summary;
}
// END method

// method: get site summary
function get_site_summary_array($start_uts=0, $stop_uts=0, $site_id=0)
{
	$SUMMARY = array();		// return
	$REPORT = array();
	$META = array();
	
	// defaults
	$this->site_id = ( !empty($site_id) ) ? $site_id : $this->site_id;
	if ( empty($start_uts) ) $start_uts = time() - $this->week_sec;
	if ( empty($stop_uts) ) $stop_uts = time() - $this->day_sec;
	
	if ( $this->debug ) $this->print_d("preparing site summary for site #{$this->site_id}");

	// Get Reports
	foreach ( $this->REPORTS as $greqo_id => $ga_id )
	{
		$REPORT[] = $this->get_report($greqo_id, $start_uts, $stop_uts, $this->site_id);
	}
	
	// Organize Reports
	$SUMMARY = $this->_organize_summary($REPORT);
	
	return $SUMMARY;
}
// end METHOD

// method: get report
function get_report($greqo_id, $start_uts=0, $stop_uts=0, $site_id=0)
{
	$REPORT = array();
	
	// defaults
	$this->site_id = ( !empty($site_id) ) ? $site_id : $this->site_id;
	if ( empty($start_uts) ) $start_uts = time() - $this->week_sec;
	if ( empty($stop_uts) ) $stop_uts = time() - $this->day_sec;
	
	if ( $this->debug ) $this->print_d("preparing report <b>{$greqo_id}</b> for site #{$this->site_id}");
	
	// is this a valid report
	if ( !isset($this->REPORTS[$greqo_id]) )
	{
		trigger_error("invalid greqo report id [$greqo_id]");
		return 0;
	}
	
	// set report variables
	$id = $this->site_id;	
	$rpt = $this->REPORTS[$greqo_id][0];
	$fmt = $this->REPORTS[$greqo_id][1];
	$cmp = ( isset($this->REPORTS[$greqo_id][2]) ) ? $this->REPORTS[$greqo_id][2] : 0;
	$pdr1 = $this->uts_to_date($start_uts);
	$pdr2 = $this->uts_to_date($stop_uts);
	
	// request report
	if ( !$report_data = $this->request_report_from_google($id, $rpt, $fmt, $pdr1, $pdr2, $cmp) )
	{
		$this->_trigger_error('unable to get report data', E_USER_WARNING);
		return 0;
	}
	
	// parse report
	if ( !$REPORT = $this->_parse_report($report_data, $fmt) )
	{
		$this->_trigger_error('unable to parse report data', E_USER_WARNING);
		return 0;
	}
	
	// add greqo id and cache id
	$REPORT['greqo_id'] = $greqo_id;
	
	// return
	return $REPORT;
}
// END method

// method: request report from google
function request_report_from_google($site_id, $rpt, $fmt=0, $pdr1=0, $pdr2=0, $cmp=0, $trows=10)
{
	$report_data = '';		// return

	// sanity checks
	if ( empty($rpt) )
	{
		$this->_trigger_error('must set report name (rpt)', E_USER_WARNING);
		return 0;
	}
	if ( empty($pdr1) )
	{
		$this->_trigger_error('must set date range start (pdr1 : YYMMDD)', E_USER_WARNING);
		return 0;
	}
	if ( empty($pdr2) )
	{
		$this->_trigger_error('must set date range end (pdr2 : YYMMDD)', E_USER_WARNING);
		return 0;
	}
	if ( empty($fmt) )
	{
		$this->_trigger_error('no format set, will set to CSV', E_USER_WARNING);
		$fmt = $this->GA_FMT_CSV;
	}
	
	// set cache id
	$cache_id = 'ga_' . $site_id . $rpt . $fmt . $pdr1 . $pdr2 . 'c' . $cmp . 't' . $trows;
	
	// check for cache
	if ( $this->debug ) $this->print_d("looking for cache (id:{$cache_id}) for csv report");
	if ( $report_csv_data = $this->GreqoCacher->get($cache_id) )
	{
		if ( $this->debug ) $this->print_d("found cache (id:{$cache_id}) for report", 'green');
		return $report_csv_data;
	}
	else
	{
		if ( $this->debug ) $this->print_d("cache (id:{$cache_id}) not found, will request new report");
	}
	
	// log in
	if ( !$this->is_logged_in ) $this->login();
	
	// prep check
	if ( !$this->_is_report_request_prepared() ) return 0;
	
	// prepare compare variables
	if ( $cmp )
	{
		$uts1 = $this->date_to_uts($pdr1);
		$uts2 = $this->date_to_uts($pdr2);
		$pdr_span = $uts2 - $uts1;
		$cmp_uts2 = $uts1 - $this->day_sec;
		$cmp_uts1 = $cmp_uts2 - $pdr_span;
		$cdr1 = $this->uts_to_date($cmp_uts1);
		$cdr2 = $this->uts_to_date($cmp_uts2);
	}
	$cmp_ga = ( $cmp ) ? $this->GA_CMP_DR : $this->GA_CMP_AVG;
	$url_cdr = ( $cmp ) ? "&cdr={$cdr1}-{$cdr2}" : '';	
	
	// set request url
	$url_query = "fmt={$fmt}&id={$site_id}&pdr={$pdr1}-{$pdr2}{$url_cdr}&cmp={$cmp_ga}&trows={$trows}&rpt={$rpt}";
	$request_url = $this->url_report_request . $url_query;

	// prepare request
	$this->GreqoClient->setMethod(HTTP_REQUEST_METHOD_GET);
	$this->GreqoClient->setURL($request_url);
	
	// process request
	if ( $this->debug ) $this->print_d('<b>' . __FUNCTION__ . ' sending request to ' . $request_url . '</b>', 'blue');
	$this->GreqoResponse = $this->GreqoClient->sendRequest();
	if ( PEAR::isError($this->GreqoResponse) )
	{
		$this->_trigger_client_error('report request failed');	
		return 0;
	}
	
	// get csv data
	$report_data = $this->GreqoClient->getResponseBody();
	if ( $this->debug ) $this->print_d("<pre>executive_report_csv: \n" . htmlspecialchars($report_data) . '</pre>');
	
	// cache report
	if ( $this->GreqoCacher->save($report_data, $cache_id) )
	{
		if ( $this->debug ) $this->print_d("report has been cached (id:{$cache_id}) for {$this->GreqoCacher->_lifeTime} s", 'green');
	}
	else
	{
		if ( $this->debug ) $this->print_d("unable to cache (id:{$cache_id})", 'yellow');
	}
	
	return $report_data;
}
// END method

// method: request account list
function request_account_list()
{
	$ACCOUNT = array();		// return
	
	// log in
	if ( !$this->is_logged_in ) $this->login();
	if ( !$this->is_logged_in ) { return 0; }
	
	// prep check
	if ( !$this->_is_login_prepared() ) return 0;
	
	// set request url
	$request_url = $this->url_home . 'report';

	// prepare request
	$this->GreqoClient->setMethod(HTTP_REQUEST_METHOD_GET);
	$this->GreqoClient->setURL($request_url);
	
	// process request
	if ( $this->debug ) $this->print_d('<b>' . __FUNCTION__ . ' sending account list request to ' . $request_url . '</b>', 'blue');
	$this->GreqoResponse = $this->GreqoClient->sendRequest();
	if ( PEAR::isError($this->GreqoResponse) )
	{
		$this->_trigger_client_error('account list request failed');	
		return 0;
	}
	
	// parse body
	$response = $this->GreqoClient->getResponseBody();
	if ( $this->debug ) $this->print_d("<pre>account list request response: \n" . htmlspecialchars($response) . '</pre>');
	
	// unexpected response
	if ( !strpos($response, 'name="account_list"') )
	{
		$this->_trigger_error('unexpected response: parse cue missing [name="account_list"]');
		return 0;
	}
	
	// parse list (credit: tantan_GoogleAnalytics)
	$response = substr($response, strpos($response, "name=\"account_list\""));
	$response = substr($response, 0, strpos($response, "</select>"));
	preg_match_all("/<option.*value=\"(.*)\".*>(.*)<\/option>/isU", $response, $MATCH);
	
	// package data
	foreach ( $MATCH[1] as $n => $account_id )
	{
		$account_name = ( isset($MATCH[2][$n]) ) ? $MATCH[2][$n] : 0;
		if ( !empty($account_id) && !empty($account_name) ) $ACCOUNT[$account_name] = $account_id;
	}
	
	return $ACCOUNT;
}
// END method

// method: request profile list
function request_profile_list($account_id=null)
{
	$PROFILE = array();		// return
	
	// log in
	if ( !$this->is_logged_in ) $this->login();
	if ( !$this->is_logged_in ) { return 0; }
	
	// prep check
	if ( !$this->_is_login_prepared() ) return 0;
	
	// set request url
	$request_url = $this->url_home . "admin?scid={$account_id}";

	// prepare request
	$this->GreqoClient->setMethod(HTTP_REQUEST_METHOD_GET);
	$this->GreqoClient->setURL($request_url);
	
	// process request
	if ( $this->debug ) $this->print_d('<b>' . __FUNCTION__ . ' sending profile list request to ' . $request_url . '</b>', 'blue');
	$this->GreqoResponse = $this->GreqoClient->sendRequest();
	if ( PEAR::isError($this->GreqoResponse) )
	{
		$this->_trigger_client_error('account list request failed');	
		return 0;
	}
	
	// parse body
	$response = $this->GreqoClient->getResponseBody();
	if ( $this->debug ) $this->print_d("<pre>profile list request response: \n" . htmlspecialchars($response) . '</pre>');
	
	// unexpected response
	$parse_cue = 'name="profile_list"';
	if ( !strpos($response, $parse_cue) )
	{
		$this->_trigger_error('unexpected response: parse cue missing [$parse_cue]');
		return 0;
	}
	
	// parse list (credit: tantan_GoogleAnalytics)
	$response = substr($response, strpos($response, $parse_cue));
	$response = substr($response, 0, strpos($response, "</select>"));
	preg_match_all("/<option.*value=\"(.*)\".*>(.*)<\/option>/isU", $response, $MATCH);
	
	// package data
	foreach ( $MATCH[1] as $n => $profile_id )
	{
		$profile_name = ( isset($MATCH[2][$n]) ) ? $MATCH[2][$n] : 0;
		if ( !empty($profile_id) && !empty($profile_name) ) $PROFILE[$profile_name] = $profile_id;
	}
	
	return $PROFILE;
}
// END method

// method: convert unix timestamp to GA date format
function uts_to_date($uts)
{
	$date = '';
	if ( !$date = date('Ymd', $uts) ) $this->_trigger_error("invalid uts: $uts");
	return $date;
}
// END method

// method: convert GA date format to unix timestamp
function date_to_uts($date)
{
	$uts = 0;
	if ( !$uts = strtotime($date) ) $this->_trigger_error("invalid date: $date");
	return $uts;
}
// END method

// method: set report cache lifetime
function set_report_cache_lifetime($secs)
{
	$this->load_cacher($secs);	
}
// END method

// method: get session data
function get_session_data()
{
	// initialize session
	if ( !session_id() ) session_start();
	
	// sanity check
	if ( empty($_SESSION[$this->oid]) ) return 0;
	
	// is this the same user
	if ( isset($this->email) && ( $_SESSION[$this->oid]['email'] != $this->email ) )
	{
		$this->clear_session_data();
		return 0;
	}
	
	// get session data
	$this->email = $_SESSION[$this->oid]['email'];
	$this->is_logged_in = isset($_SESSION[$this->oid]['is_logged_in']) ? $_SESSION[$this->oid]['is_logged_in'] : 0;
	$this->GCOOKIE = $_SESSION[$this->oid]['COOKIES'];
	
	// set cookies
	if ( is_array($this->GCOOKIE) )
	{
		foreach ( $this->GCOOKIE as $_CD ) $this->GreqoClient->addCookie($_CD['name'], $_CD['value']);
	}
	
	return 1;
}
// END method

// method: set session data
function set_session_data()
{
	// initialize session
	if ( !session_id() ) session_start(); 
	$_SESSION[$this->oid] = array();
	
	// sanity check
	if ( !$this->is_logged_in ) 
	{
		trigger_error("not logged in: session data should be set only after logging in", E_USER_WARNING);
		return 0;
	}
	
	$_SESSION[$this->oid]['email'] = $this->email;
	$_SESSION[$this->oid]['is_logged_in'] = $this->is_logged_in;
	$_SESSION[$this->oid]['COOKIES'] = $this->GCOOKIE;
	return 1;
}
// END method

// method: set session data
function clear_session_data()
{
	// initialize session
	if ( !session_id() ) session_start(); 
	$_SESSION[$this->oid] = null;
	return;
}
// END method

// method: logout
function logout()
{
	$this->is_logged_in = 0;
	$this->clear_session_data();
}
// END method



/* ** PRIVATE METHODS ** */
// method: summary array to string
function _summary_array_to_string($DATA)
{
	$string = '';		// return
	$sum_keywords = '';
	$sum_sources = '';
	$sum_content = '';

	// prepare data
	$name = $DATA['META']['profile_name'];
	$dates = $DATA['META']['date_range'];
	$cf_dates = ( !empty($DATA['META']['date_range']) ) ? $DATA['META']['date_range'] : '';
	$tv = $DATA['VISITORS']['visits']['value'];
	$uv = $DATA['VISITORS']['unique_visits']['value'];
	$pv = $DATA['VISITORS']['pageviews']['value'];
	$tvy = !empty($cf_dates) ? "({$DATA['VISITORS']['visits']['submessage']} / {$DATA['VISITORS']['visits']['delta']})" : '';
	$uvy = !empty($cf_dates) ? "({$DATA['VISITORS']['unique_visits']['submessage']} / {$DATA['VISITORS']['unique_visits']['delta']})" : '';
	$pvy = !empty($cf_dates) ? "({$DATA['VISITORS']['pageviews']['submessage']} / {$DATA['VISITORS']['pageviews']['delta']})" : '';

	// sources
	if ( !empty($DATA['SOURCES']['RECORDS']) )
	{
		$sum_sources .= "<b>source/medium : visits | pg/v | time (s)</b> \n";
		foreach ( $DATA['SOURCES']['RECORDS'] as $i => $_REC )
		{
			$n = $i + 1;
			$sum_sources .= "{$n}. {$_REC['Source/Medium']} : {$_REC['Visits']} | {$_REC['Pages/Visit']} | {$_REC['Avg. Time on Site']} \n";
		}
	}
	
	// keywords
	if ( !empty($DATA['KEYWORDS']['RECORDS']) )
	{
		$sum_keywords .= "<b>keywords : visits</b> \n";
		foreach ( $DATA['KEYWORDS']['RECORDS'] as $i => $_REC )
		{
			$n = $i + 1;
			$sum_keywords .= "{$n}. {$_REC['Keyword']} : {$_REC['Visits']}\n";
		}
	}
	
	// content
	if ( !empty($DATA['CONTENT']['RECORDS']) )
	{
		$sum_content .= "<b>page (url) : views | unique | time (s)</b> \n";
		foreach ( $DATA['CONTENT']['RECORDS'] as $i => $_REC )
		{
			$n = $i + 1;
			$sum_content .= "{$n}. {$_REC['URL']} : {$_REC['Pageviews']} | {$_REC['Unique Pageviews']} | {$_REC['Time on Page']} \n";
		}
	}
	
	// build
	$string = <<<TEXT

Greqo Analytics Summary for $name

DATES: $dates 
VISITORS: $tv $tvy
PAGE VIEWS: $pv $pvy
UNIQUE VISITORS: $uv $uvy

SOURCES
$sum_sources

KEYWORDS
$sum_keywords

CONTENT
$sum_content
	
TEXT;

	return $string;
}
// END method

// method: organize summary
function _organize_summary($REPORT)
{
	$SUMMARY = array();		// return
	$SECTION = array();
	
	if ( $this->debug ) $this->print_d('organizing summar of array: ' . $this->print_r($REPORT));
	
	// Meta Data
	$cf_dates = isset($REPORT[0]['CompareDateRange']) ? $REPORT[0]['CompareDateRange'] : '';
	$SUMMARY['META'] = array
	(
		'profile_name' => $REPORT[0]['ProfileName'],
		'site_id' => $this->site_id,
		'date_range' => $REPORT[0]['PrimaryDateRange'],
		'date_compare' => $cf_dates,
		'date_range_ymd' => $REPORT[1]['dates'],
		'report_date' => date('Y-m-d H:i:s'),
		'uts' => time(),
	);
	
	// Visitor Data
	$SUMMARY['VISITORS'] = array
	(
		'greqo_id' => $REPORT[0]['greqo_id'],
		'report_name' => $REPORT[0]['Name'],
		#'cache_id' => $REPORT[0]['cache_id'],
		'visits' => $REPORT[0]['Visits'],
		'unique_visits' => $REPORT[0]['Absolute Unique Visitors'],
		'pageviews' => $REPORT[0]['Pageviews'],
		'pageview_per_visit' => $REPORT[0]['Average Pageviews'],
		'bounce_rate' => $REPORT[0]['Bounce Rate'],
		'new_visits' => $REPORT[0]['New Visits'],
		'time_per_visit' => $REPORT[0]['Time on Site'],
	);
	
	// Other Reports
	for ( $i=1; $i<count($REPORT); $i++ )
	{
		$key = strtoupper($REPORT[$i]['greqo_id']);
		$SUMMARY[$key] = array
		(
			'greqo_id' => $REPORT[$i]['greqo_id'],
			'report_name' => $REPORT[$i]['name'],
			'cache_id' => $REPORT[$i]['cache_id'],
			'num_cols' => $REPORT[$i]['SECTION'][1]['num_cols'],
			'num_records' => $REPORT[$i]['SECTION'][1]['num_records'],
			'RECORDS' => $REPORT[$i]['SECTION'][1]['RECORD'],
		);
	}
		
	return $SUMMARY;
}
// END method

// method: parse report
function _parse_report($report_data, $format)
{
	$REPORT = array();		// return
	
	if ( $format == $this->GA_FMT_XML )
	{
		$REPORT = $this->_quick_parse_report_xml($report_data);
	}
	elseif ( $format == $this->GA_FMT_CSV )
	{
		$REPORT = $this->_parse_report_csv($report_data);
	}
	else
	{
		$this->_trigger_error("unknown format [$format]");
		return 0;
	}
	
	return $REPORT;
}
// END method

// method: parse report csv (v.2)
function _parse_report_csv($raw_csv)
{
	$REPORT = array();		// return
	$_FLAG['parsed_header'] = 0;
	
	if ( $this->debug ) $this->print_d('parsing report');
	
	// get sections
	$_SECTION = explode("\n\n", $raw_csv);
	if ( $this->debug ) $this->print_d('parsed report into ' . count($_SECTION) . ' sections');
	
	// get header
	$_LINES = explode("\n", $_SECTION[0]);
	foreach ( $_LINES as $line )
	{
		if ( !empty($line) && ( strpos($line, '# -') === false ) ) $_HEADER[] = $line;
	}
	$REPORT['profile'] = $_HEADER[0];
	$REPORT['name'] = $_HEADER[1];
	
	// get dates
	if ( $match = preg_match_all('|"([^"]+)"|', $_HEADER[2], $_DATES) )
	{
		foreach ( $_DATES[1] as $_date ) $_YMD[] = date('Ymd', strtotime($_date));
		$REPORT['dates'] = "{$_YMD[0]} - {$_YMD[1]}";
	}
	else
	{
		$REPORT['dates'] = $_HEADER[2];
	}
	
	// parse remainings sections
	for ( $i=1; $i<count($_SECTION); $i++ )
	{
		$REPORT['SECTION'][] = $this->_parse_report_section($_SECTION[$i]);
	}
	
	return $REPORT;
}
// END method

// method: parse report section
function _parse_report_section($raw_section_csv)
{
	$SECTION = array();		// return
	$_LINES = explode("\n", $raw_section_csv);
	$dcolor = '#999';
	
	if ( $this->debug ) $this->print_d("parsing report section:<pre style='color:#333;'>$raw_section_csv</pre>");
	
	foreach ( $_LINES as $line )
	{
		// trim line
		$line = trim($line);
		
		if ( $this->debug ) $this->print_d('<br />parsing line: ' . htmlspecialchars($line), '#996');
		
		// ignore empty or comment lines
		if ( empty($line) || ( strpos($line, '#-') !== false ) || ( strpos($line, '# -') !== false ) )
		{
			if ( $this->debug ) $this->print_d('line id\'d as empty', $dcolor);
			continue;
		}
		
		// header rows
		elseif ( $line{0} == '#' )
		{
			if ( $this->debug ) $this->print_d('line id\'d as header', $dcolor);

			// package *last* dataset
			if ( !empty($dataset_title) )
			{
				$DATASET[] = array
				(
					'title' => $dataset_title,
					'num_cols' => count($COLUMN),
					'num_records' => count($RECORD),
					'COLUMN' => $COLUMN,
					'RECORD' => $RECORD
				);
				if ( $this->debug ) $this->print_d("packaging dataset '$dataset_title'");
			}
				
			// init new dataset
			$dataset_title = trim(substr($line, 2));
			$COLUMN = array();
			$RECORD = array();
			
			// we don't care about graphs
			if ( $dataset_title == 'Graph' ) 
			{
				if ( $this->debug ) $this->print_d('graph section are ignored and return \'graph\'', 'magenta');
				return 'graph';
			}										
		}
		
		// column row
		elseif ( empty($COLUMN) )
		{
			if ( $this->debug ) $this->print_d('line id\'d as column');
			
			// get column names
			$COLUMN = explode(',', $line);
			foreach ($COLUMN as $ck => $cv) $COLUMN[$ck] = trim($cv);
		}
		
		// record row
		else
		{
			if ( $this->debug ) $this->print_d('line id\'d as record');
			
			$_NEW_RECORD = array();
			$_LINE_SEG = explode(',', $line);
			foreach ( $_LINE_SEG as $i => $value ) $_NEW_RECORD['value'.$i] = trim($value);
			$RECORD[] = $_NEW_RECORD;
			if ( $this->debug ) $this->print_d('record: ' . print_r($_NEW_RECORD));
		}
	}
	
	// package the final dataset
	$DATASET[] = array
	(
		'title' => $dataset_title,
		'num_cols' => count($COLUMN),
		'num_records' => count($RECORD),
		'COLUMN' => $COLUMN,
		'RECORD' => $RECORD
	);
	
	// package datasets
	foreach ( $DATASET as $n => $_SET )
	{
		$SECTION['title'] = $_SET['title'];
		$SECTION['num_cols'] = $_SET['num_cols'];
		$SECTION['num_records'] = $_SET['num_records'];
		$SECTION['COLUMN'] = $_SET['COLUMN'];
		$SECTION['RECORD'] = array();
		
		// repackage records
		if ( empty($_SET['RECORD']) )
		{
			$SECTION['RECORD'] = 0;
		}
		else
		{
			foreach ( $_SET['RECORD'] as $i => $_COLS )
			{
				$SECTION['RECORD'][$i] = array();
				for ( $j=0; $j<count($_COLS); $j++ ) $SECTION['RECORD'][$i][$_SET['COLUMN'][$j]] = $_COLS['value'.$j];
			}
		}
	}
	
	if ( $this->debug ) $this->print_d($this->print_r($SECTION));
	return $SECTION;	
}
// END method

// method: quick parse report xml
function _quick_parse_report_xml($xml_string)
{
	$DATA = array();
	
	// control array
	$TITLE_METAKEY = array('Name','ProfileName','PrimaryDateRange','CompareDateRange');
	
	// regex
	$_REGEX['title'] = '|<Title[^>]+>(.*)</Title>|iU';
	$_REGEX['title_data'] = '|<([^>]+)>(.*)</[^>]+>|iU';
	$_REGEX['item_summary'] = '|<ItemSummary[^>]+>(.*)</ItemSummary>|iU';
	$_REGEX['key'] = '|<message>(.*)</message>|iU';
	$_REGEX['value'] = '|<SummaryValue>(.*)</SummaryValue>|iU';
	$_REGEX['submessage'] = '|<SubMessage>(.*)</SubMessage>|iU';
	$_REGEX['delta'] = '|<Delta>(.*)</Delta>|iU';
	
	// parse title
	if ( !preg_match_all($_REGEX['title'], $xml_string, $TITLE) ) trigger_error('unable to parse title');
	if ( $this->debug ) $this->_dump_array($TITLE); 
	
	if ( !empty($TITLE) )
	{
		$xml_title = $TITLE[1][0];
		preg_match_all($_REGEX['title_data'], $xml_title, $TITLE_DATA);
		foreach ( $TITLE_DATA[1] as $i => $_metakey )
		{
			if ( in_array($_metakey,$TITLE_METAKEY) )
			{
				$DATA[$_metakey] = $TITLE_DATA[2][$i];
			}  
		}
	}
	if ( $this->debug ) $this->_dump_array($TITLE_DATA); 

	// parse item summaries
	$matched = preg_match_all($_REGEX['item_summary'], $xml_string, $ITEM_XML);
	if ( $this->debug ) $this->_dump_array($ITEM_XML); 
	if ( !$matched ) return $DATA;	
	
	foreach ( $ITEM_XML[1] as $_xml )
	{
		if ( $this->debug ) $this->print_d('parsing : ' . htmlspecialchars($_xml)); 
		$key = $val = $sub = $delta = '';
		if ( preg_match($_REGEX['key'], $_xml, $_KEY) ) $key = strip_tags($_KEY[0]);
			else trigger_error('unable to parse item key');
		if ( preg_match($_REGEX['value'], $_xml, $_VAL) )  $val = strip_tags($_VAL[0]);
			else trigger_error('unable to parse item value');
		
		// compare values
		if ( preg_match($_REGEX['submessage'], $_xml, $_SUB) ) $sub = strip_tags($_SUB[0]);
		if ( preg_match($_REGEX['delta'], $_xml, $_DEL) ) $delta = strip_tags($_DEL[0]);
			
		if ( !empty($key) )
		{
			$DATA[$key]['value'] = $val;
			if ( !empty($sub) ) $DATA[$key]['submessage'] = $sub;
			if ( !empty($delta) ) $DATA[$key]['delta'] = $delta;
		}		
	}
	
	if ( $this->debug ) $this->_dump_array($DATA); 
	return $DATA;
}
// END method

// method: is report request prepared
function _is_report_request_prepared()
{
	if ( !$this->_is_login_prepared() ) return 0;
	
	if ( !$this->is_logged_in )
	{
		trigger_error('must login before requesting a report', E_USER_WARNING);
		return 0;
	}
	if ( empty($this->site_id) )
	{
		trigger_error('site id must be set (login and check profile links if unsure)', E_USER_WARNING);
		return 0;
	}
	
	return 1;
}
// END method

// method: trigger error
function _trigger_error($message)
{
	$this->GreqoClient->_postData['Passwd'] = $this->password = '**scrubbed**';
	trigger_error('greqo analytics error: ' . $message, E_USER_WARNING);		
	if ( $this->debug ) 
	{
		$this->print_d('<h4>Analytics Object</h4>' . $this->print_r($this), 'magenta');
	}	
}
// END method

// method: dump array
function _dump_array($ARRAY)
{
	$dump = htmlspecialchars(print_r($ARRAY,1));
	$this->print_d("<pre>$dump</pre>", '#33c');
	return;
}
// END method

function _set_filename() { $this->_filename = basename(__FILE__); }
function _set_dirpath() { $this->_dirpath = dirname(__FILE__) . $this->DS; }

} // end class
/*____________________________________________________________________________*/

?>
